package game.coord;

import game.util.Logger;

import java.awt.Dimension;
import java.awt.Point;

public class MapGenerator
{
    public static Point getRight(Point position, Dimension maxSize)
    {
        // RIGHT:
        Point RIGHT = new Point(position.x + 1, position.y);
        if (RIGHT.getX() >= maxSize.width)
        {
            RIGHT.move(0, RIGHT.y);
        }
        return RIGHT;
    }
    
    public static Point getBottom(Point position, Dimension maxSize)
    {
        // BOTTOM:
        Point BOTTOM = new Point(position.x, position.y + 1);
        if (BOTTOM.getY() >= maxSize.height)
        {
            BOTTOM.move(BOTTOM.x, 0);
        }
        return BOTTOM;
    }
    
    public static Point getLeft(Point position, Dimension maxSize)
    {
        // LEFT:
        Point LEFT = new Point(position.x - 1, position.y);
        if (LEFT.getX() < 0)
        {
            LEFT.move(maxSize.width - 1, LEFT.y);
        }
        return LEFT;
    }
    
    public static Point getTop(Point position, Dimension maxSize)
    {
        // TOP:
        Point TOP = new Point(position.x, position.y - 1);
        if (TOP.getY() < 0)
        {
            TOP.move(TOP.x, maxSize.height - 1);
        }
        return TOP;
    }
    
    public static Point getBottomLeft(Point position, Dimension maxSize)
    {
        // BOTTOMLEFT:
        Point BOTTOMLEFT = new Point(getLeft(position, maxSize).x, getBottom(position, maxSize).y);
        return BOTTOMLEFT;
    }
    
    public static Point getBottomRight(Point position, Dimension maxSize)
    {
        // BOTTOMRIGHT:
        Point BOTTOMRIGHT = new Point(getRight(position, maxSize).x, getBottom(position, maxSize).y);
        return BOTTOMRIGHT;
    }
    
    public static Point getTopLeft(Point position, Dimension maxSize)
    {
        // TOPLEFT:
        Point TOPLEFT = new Point(getLeft(position, maxSize).x, getTop(position, maxSize).y);
        return TOPLEFT;
    }
    
    public static Point getTopRight(Point position, Dimension maxSize)
    {
        // TOPRIGHT:
        Point TOPRIGHT = new Point(getRight(position, maxSize).x, getTop(position, maxSize).y);
        return TOPRIGHT;
    }
    
    public static <TYPE> MultiArrayContainer<TYPE> generateList(Dimension size, Class<TYPE> typeClass, TYPE defaultValue)
    {
        Logger.debug("GENERATE LIST (", size, "; class = ", typeClass, ")");
        MultiArrayContainer<TYPE> list = new MultiArrayContainer<TYPE>(size);
        for (int y = 0; y < size.height; y++)
        {
            for (int x = 0; x < size.width; x++)
            {
                TYPE instance = defaultValue;
                if (instance == null)
                {
                    try
                    {
                        instance = typeClass.newInstance();
                    }
                    catch (InstantiationException | IllegalAccessException ex)
                    {
                        ex.printStackTrace();
                    }
                }
                list.setValue(new Point(x, y), instance);
            }
        }
        return list;
    }
    
    public static <TYPE> MultiArrayContainer<TYPE> generateList(Dimension size, Class<TYPE> typeClass)
    {
        return generateList(size, typeClass, null);
    }
    
    @SuppressWarnings("unchecked")
    public static <TYPE> MultiArrayContainer<TYPE> generateList(Dimension size, TYPE defaultValue)
    {
        return generateList(size, ((Class<TYPE>)defaultValue.getClass()), defaultValue);
    }
    
    public static <TYPE extends MooreCell> MultiArrayContainer<TYPE> generateMooreMap(Dimension size, Class<TYPE> cellClass)
    {
        MultiArrayContainer<TYPE> list = generateList(size, cellClass);
        rebuildMooreMap(list);
        return list;
    }
    
    public static <TYPE extends MooreCell> MultiArrayContainer<TYPE> generateMooreMap(Dimension size, TYPE defaultValue)
    {
        MultiArrayContainer<TYPE> list = generateList(size, defaultValue);
        rebuildMooreMap(list);
        return list;
    }
    
    public static <TYPE extends MooreCell> void rebuildMooreMap(MultiArrayContainer<TYPE> list)
    {
        for (IterablePoint<TYPE> i = list.getIterator(); i.hasNext();)
        {
            Point position = i.getCurrentPoint();
            TYPE cell = i.next();
            cell.setRight(list.getValue(getRight(position, list.getSize())));
            cell.setBottom(list.getValue( getBottom(position, list.getSize())));
            cell.setLeft(list.getValue( getLeft(position, list.getSize())));
            cell.setTop(list.getValue( getTop(position, list.getSize())));
            cell.setBottomLeft(list.getValue( getBottomLeft(position, list.getSize())));
            cell.setBottomRight(list.getValue( getBottomRight(position, list.getSize())));
            cell.setTopLeft(list.getValue( getTopLeft(position, list.getSize())));
            cell.setTopRight(list.getValue( getTopRight(position, list.getSize())));
        }
    }
    
    public static <TYPE extends VonNeumannCell<TYPE>> MultiArrayContainer<TYPE> generateVonNeumannMap(Dimension size, Class<TYPE> cellClass)
    {
        MultiArrayContainer<TYPE> list = generateList(size, cellClass);
        rebuildVonNeumannMap(list);
        return list;
    }
    
    public static <TYPE extends VonNeumannCell<TYPE>> MultiArrayContainer<TYPE> generateVonNeumannMap(Dimension size, TYPE defaultValue)
    {
        MultiArrayContainer<TYPE> list = generateList(size, defaultValue);
        rebuildVonNeumannMap(list);
        return list;
    }
    
    public static <TYPE extends VonNeumannCell<TYPE>> void rebuildVonNeumannMap(MultiArrayContainer<TYPE> list)
    {
        for (IterablePoint<TYPE> i = list.getIterator(); i.hasNext();)
        {
            Point position = i.getCurrentPoint();
            TYPE cell = i.next();
            cell.setRight(list.getValue(getRight(position, list.getSize())));
            cell.setBottom(list.getValue(getBottom(position, list.getSize())));
            cell.setLeft(list.getValue(getLeft(position, list.getSize())));
            cell.setTop(list.getValue(getTop(position, list.getSize())));
        }
    }
    
    public static MooreCellWithChildren generateMooreCellWithChildren(final int generations)
    {
        // and there was god, creator of the universe
        MooreCellWithChildren god = generateSimpleMooreCellWithChildren(null, 0);
        generateMooreCellWithChildren(generations, god);
        return god;
    }
    
    private static void generateMooreCellWithChildren(final int generations, final MooreCellWithChildren god)
    {
        Logger.debug("CREATING GENERATION ", generations, " ", MooreCellWithChildren.countChilds(generations));
        if (god.getGeneration() < generations)
        {
            // and his children
            god.setChild(generateSimpleMooreCellWithChildren(god, MooreCellWithChildren.TOPLEFT));
            god.setChild(generateSimpleMooreCellWithChildren(god, MooreCellWithChildren.TOP));
            god.setChild(generateSimpleMooreCellWithChildren(god, MooreCellWithChildren.TOPRIGHT));
            god.setChild(generateSimpleMooreCellWithChildren(god, MooreCellWithChildren.LEFT));
            god.setChild(generateSimpleMooreCellWithChildren(god, MooreCellWithChildren.CENTER));
            god.setChild(generateSimpleMooreCellWithChildren(god, MooreCellWithChildren.RIGHT));
            god.setChild(generateSimpleMooreCellWithChildren(god, MooreCellWithChildren.BOTTOMLEFT));
            god.setChild(generateSimpleMooreCellWithChildren(god, MooreCellWithChildren.BOTTOM));
            god.setChild(generateSimpleMooreCellWithChildren(god, MooreCellWithChildren.BOTTOMRIGHT));
            // and they all had children
            for (int position = 1; position < 10; position++)
            {
                final int next = position;
                Thread t = new Thread(new Runnable()
                {
                    
                    @Override
                    public void run()
                    {
                        generateMooreCellWithChildren(generations, god.getChild(next));
                    }
                }); 
                t.start();
            }
        }
    }
    
    private static MooreCellWithChildren generateSimpleMooreCellWithChildren(MooreCellWithChildren parent, int position)
    {
        // and there was god, creator of the universe
        MooreCellWithChildren god = new MooreCellWithChildren(parent, position);
        // there where no gods beside him
        if (parent == null)
        {
            god.setTopLeft(null);
            god.setTop(null);
            god.setTopRight(null);
            god.setLeft(null);
            god.setRight(null);
            god.setBottomLeft(null);
            god.setBottom(null);
            god.setBottomRight(null);
        }
        else
        {
            // so he looked for his brothers and sisters
            // and asked where his place was
            switch(position)
            {
                case MooreCellWithChildren.TOPLEFT:
                {
                    god.setTopLeft(parent.getTopLeft().getChild(MooreCellWithChildren.BOTTOMRIGHT));
                    god.setTop(parent.getTop().getChild(MooreCellWithChildren.BOTTOMLEFT));
                    god.setTopRight(parent.getTop().getChild(MooreCellWithChildren.BOTTOM));
                    god.setLeft(parent.getLeft().getChild(MooreCellWithChildren.TOPRIGHT));
                    god.setRight(parent.getChild(MooreCellWithChildren.TOP));
                    god.setBottomLeft(parent.getLeft().getChild(MooreCellWithChildren.RIGHT));
                    god.setBottom(parent.getChild(MooreCellWithChildren.LEFT));
                    god.setBottomRight(parent.getChild(MooreCellWithChildren.CENTER));
                    break;
                }
                case MooreCellWithChildren.TOP:
                {
                    god.setTopLeft(parent.getTop().getChild(MooreCellWithChildren.BOTTOMLEFT));
                    god.setTop(parent.getTop().getChild(MooreCellWithChildren.BOTTOM));
                    god.setTopRight(parent.getTop().getChild(MooreCellWithChildren.BOTTOMRIGHT));
                    god.setLeft(parent.getChild(MooreCellWithChildren.TOPLEFT));
                    god.setRight(parent.getChild(MooreCellWithChildren.TOPRIGHT));
                    god.setBottomLeft(parent.getChild(MooreCellWithChildren.LEFT));
                    god.setBottom(parent.getChild(MooreCellWithChildren.CENTER));
                    god.setBottomRight(parent.getChild(MooreCellWithChildren.RIGHT));
                    break;
                }
                case MooreCellWithChildren.TOPRIGHT:
                {
                    god.setTopLeft(parent.getTop().getChild(MooreCellWithChildren.BOTTOM));
                    god.setTop(parent.getTop().getChild(MooreCellWithChildren.BOTTOMRIGHT));
                    god.setTopRight(parent.getTopRight().getChild(MooreCellWithChildren.BOTTOMLEFT));
                    god.setLeft(parent.getChild(MooreCellWithChildren.TOP));
                    god.setRight(parent.getRight().getChild(MooreCellWithChildren.TOPLEFT));
                    god.setBottomLeft(parent.getChild(MooreCellWithChildren.CENTER));
                    god.setBottom(parent.getChild(MooreCellWithChildren.RIGHT));
                    god.setBottomRight(parent.getRight().getChild(MooreCellWithChildren.LEFT));
                    break;
                }
                case MooreCellWithChildren.LEFT:
                {
                    god.setTopLeft(parent.getLeft().getChild(MooreCellWithChildren.TOPRIGHT));
                    god.setTop(parent.getChild(MooreCellWithChildren.TOPLEFT));
                    god.setTopRight(parent.getChild(MooreCellWithChildren.TOP));
                    god.setLeft(parent.getLeft().getChild(MooreCellWithChildren.RIGHT));
                    god.setRight(parent.getChild(MooreCellWithChildren.CENTER));
                    god.setBottomLeft(parent.getLeft().getChild(MooreCellWithChildren.BOTTOMRIGHT));
                    god.setBottom(parent.getChild(MooreCellWithChildren.BOTTOMLEFT));
                    god.setBottomRight(parent.getChild(MooreCellWithChildren.BOTTOM));
                    break;
                }
                case MooreCellWithChildren.CENTER:
                {
                    god.setTopLeft(parent.getChild(MooreCellWithChildren.TOPLEFT));
                    god.setTop(parent.getChild(MooreCellWithChildren.TOP));
                    god.setTopRight(parent.getChild(MooreCellWithChildren.TOPRIGHT));
                    god.setLeft(parent.getChild(MooreCellWithChildren.LEFT));
                    god.setRight(parent.getChild(MooreCellWithChildren.RIGHT));
                    god.setBottomLeft(parent.getChild(MooreCellWithChildren.BOTTOMLEFT));
                    god.setBottom(parent.getChild(MooreCellWithChildren.BOTTOM));
                    god.setBottomRight(parent.getChild(MooreCellWithChildren.BOTTOMRIGHT));
                    break;
                }
                case MooreCellWithChildren.RIGHT:
                {
                    god.setTopLeft(parent.getChild(MooreCellWithChildren.TOP));
                    god.setTop(parent.getChild(MooreCellWithChildren.TOPRIGHT));
                    god.setTopRight(parent.getRight().getChild(MooreCellWithChildren.TOPLEFT));
                    god.setLeft(parent.getChild(MooreCellWithChildren.CENTER));
                    god.setRight(parent.getRight().getChild(MooreCellWithChildren.LEFT));
                    god.setBottomLeft(parent.getChild(MooreCellWithChildren.BOTTOM));
                    god.setBottom(parent.getChild(MooreCellWithChildren.BOTTOMRIGHT));
                    god.setBottomRight(parent.getRight().getChild(MooreCellWithChildren.BOTTOMLEFT));
                    break;
                }
                case MooreCellWithChildren.BOTTOMLEFT:
                {
                    god.setTopLeft(parent.getLeft().getChild(MooreCellWithChildren.RIGHT));
                    god.setTop(parent.getChild(MooreCellWithChildren.LEFT));
                    god.setTopRight(parent.getChild(MooreCellWithChildren.CENTER));
                    god.setLeft(parent.getLeft().getChild(MooreCellWithChildren.BOTTOMRIGHT));
                    god.setRight(parent.getChild(MooreCellWithChildren.BOTTOM));
                    god.setBottomLeft(parent.getBottomLeft().getChild(MooreCellWithChildren.TOPRIGHT));
                    god.setBottom(parent.getBottom().getChild(MooreCellWithChildren.TOPLEFT));
                    god.setBottomRight(parent.getBottom().getChild(MooreCellWithChildren.TOP));
                    break;
                }
                case MooreCellWithChildren.BOTTOM:
                {
                    god.setTopLeft(parent.getChild(MooreCellWithChildren.LEFT));
                    god.setTop(parent.getChild(MooreCellWithChildren.CENTER));
                    god.setTopRight(parent.getChild(MooreCellWithChildren.RIGHT));
                    god.setLeft(parent.getChild(MooreCellWithChildren.BOTTOMLEFT));
                    god.setRight(parent.getChild(MooreCellWithChildren.BOTTOMRIGHT));
                    god.setBottomLeft(parent.getBottom().getChild(MooreCellWithChildren.TOPLEFT));
                    god.setBottom(parent.getBottom().getChild(MooreCellWithChildren.TOP));
                    god.setBottomRight(parent.getBottom().getChild(MooreCellWithChildren.TOPRIGHT));
                    break;
                }
                case MooreCellWithChildren.BOTTOMRIGHT:
                {
                    god.setTopLeft(parent.getChild(MooreCellWithChildren.CENTER));
                    god.setTop(parent.getChild(MooreCellWithChildren.RIGHT));
                    god.setTopRight(parent.getRight().getChild(MooreCellWithChildren.LEFT));
                    god.setLeft(parent.getChild(MooreCellWithChildren.BOTTOM));
                    god.setRight(parent.getRight().getChild(MooreCellWithChildren.BOTTOMLEFT));
                    god.setBottomLeft(parent.getBottom().getChild(MooreCellWithChildren.TOP));
                    god.setBottom(parent.getBottom().getChild(MooreCellWithChildren.RIGHT));
                    god.setBottomRight(parent.getBottomRight().getChild(MooreCellWithChildren.TOPLEFT));
                    break;
                }
            }
        }
        // god had nine undergods
        god.setChild(new MooreCellWithChildren(god, MooreCellWithChildren.CENTER));
        god.setChild(new MooreCellWithChildren(god, MooreCellWithChildren.TOPLEFT));
        god.setChild(new MooreCellWithChildren(god, MooreCellWithChildren.TOP));
        god.setChild(new MooreCellWithChildren(god, MooreCellWithChildren.TOPRIGHT));
        god.setChild(new MooreCellWithChildren(god, MooreCellWithChildren.LEFT));
        god.setChild(new MooreCellWithChildren(god, MooreCellWithChildren.RIGHT));
        god.setChild(new MooreCellWithChildren(god, MooreCellWithChildren.BOTTOMLEFT));
        god.setChild(new MooreCellWithChildren(god, MooreCellWithChildren.BOTTOM));
        god.setChild(new MooreCellWithChildren(god, MooreCellWithChildren.BOTTOMRIGHT));
        return god;
    }
}
